#include "ogl_font.h"
#include "oglgraphics/ogl_graphics.h"
#include "graphics/graphics_util.h"
#include "graphics/graphics.h"

#include "../sleipner_config.h"

extern "C"
{
    
#ifdef _SDL2
    
#include "SDL2/include/SDL.h"
#ifdef WIN32
#include "glee/glee.h"
    //#include "SDL-1.2.13/include/SDL_opengl.h"
#else
#ifdef _OPENGLES2
#include "SDL2/include/SDL_opengles2.h"
#else
#include "SDL2/include/SDL_opengles.h"
#endif
#endif
    
#else
    
    
    #include "SDL-1.2.13/include/SDL.h"
    #ifndef _PLATFORM_IOS
        #include "SDL-1.2.13/include/SDL_opengl.h"
    //#include "SDL-1.2.13/include/SDL_opengl.h"
    #else
        #include "SDL-1.2.13/include/SDL_opengles.h"
    #endif
    
#endif
    

    
}

// free type lib header files
#include "freetype/include/ft2build.h"
#include "freetype/include/freetype/freetype.h"
#include "freetype/include/freetype/ftglyph.h"



#include "math/vec2.h"
#include "lang/lang.h"
#include "num/named_set.h"

#include "util/common_macros.h"		// macros
#include "util/log.h"		// macros
#include "util/mmgr.h"		// Needs to come in early
#include "util/timer.h"		// Needs to come in early


#include <cassert>


oglFont::oglFont( const std::string& Name ) :
Font(Name),  		
m_FreeTypeFace(NULL),
m_AltFreeTypeFace(NULL),
m_Texture(0),
m_CacheGlyphC(0)

{
	m_Highlight = 0xFFFFFF00;
	
}

oglFont::~oglFont()
{
	DeInit();
	
}


void	oglFont::DeInit()
{
    
    if(m_Texture)
		glDeleteTextures(1 , &m_Texture);
    
    for (unsigned int i=0; i<m_pEmpty.size(); i++)
	{		
		delete m_pEmpty[i];
	}
	m_pEmpty.resize(0);
    
    m_GlyphMap.ClearSet();
    
	m_FontData.Erase();
	m_AltFontData.Erase();
}

void	oglFont::Recreate()
{
    Font::Recreate();
	    
    for (unsigned int i=0; i<m_pEmpty.size(); i++)
	{		
		delete m_pEmpty[i];
	}
	m_pEmpty.resize(0);
    
    m_GlyphMap.ClearSet();
    if(m_Texture)
		glDeleteTextures(1 , &m_Texture);
	
	Resize( m_Face,m_Width,m_Height,m_Weight,0 );
	
}


void	oglFont::Resize(const std::string& _Face, int Width, int Height, int Weight, unsigned long CharSet)
{
	
	m_Face = _Face;
	
	//m_TexWidth =   GraphicsUtil::WIDTH;
	//m_TexHeight =  GraphicsUtil::HEIGHT;
    
	m_TexWidth =   NextPowerof2(GetGraphics()->GetScreenWidth());
	m_TexHeight =  NextPowerof2(GetGraphics()->GetScreenHeight());

	// make it square for some devices
	if(m_TexWidth > m_TexHeight)
		m_TexHeight = m_TexWidth;
	else
		m_TexWidth = m_TexHeight;


	
	//DeInit();
	m_Height = Height;
	m_Width  = Width;
	m_Weight = Weight;
    
	//m_TexWidth = m_TexHeight = 1024;	
	
	m_QuadSize = (int)(m_Height * 2.0f );	// some glyphs are wider than they 
	int	GlyphWidth = m_TexWidth		/	m_QuadSize;
	int	GlyphHeight = m_TexHeight	/	m_QuadSize;
	for (int x=0; x<GlyphWidth; x++)
        for (int y=0; y<GlyphHeight; y++)
        {
            GlyphData* pGlyph = new GlyphData;
            pGlyph->TimeStamp	= -1;
            pGlyph->Glyph	= 0;
            pGlyph->PreOffset = 0;
            pGlyph->OffsetX = 0;
            pGlyph->Height	= pGlyph->Width = pGlyph->xp = pGlyph->yp = 0;	// doesn't matter
            pGlyph->TexX	= (x * m_QuadSize);
            pGlyph->TexY	= (y * m_QuadSize);
            pGlyph->TexW	= m_Width;		// should shrink to match exact size
            pGlyph->TexH	= m_Height;
            pGlyph->Pos		= math::Vec2(	float(pGlyph->TexX) / float(m_TexWidth), 
                                         float(pGlyph->TexY) / float(m_TexHeight));
            pGlyph->Size	= math::Vec2(	float(pGlyph->TexW) / float(m_TexWidth), 
                                         float(pGlyph->TexH) / float(m_TexHeight));
            m_pEmpty.push_back(pGlyph);
        }
    
    
        
	
	// Initialize Free Type 
	FT_Library library;
    
    if(m_FreeTypeFace == NULL)
    {
        if (FT_Init_FreeType( &library ) == 0)
        {}
        else
        {
            _LOG( MSG_ERROR , "FreeType Init Failed" );
            return;
        }
    
    
        // Opening the font file and load into memory
        vfs::VFS& FileSys = vfs::VFS::Instance();
    
        if (!FileSys.GetFile(m_Face, m_FontData))
        {
            _LOG(MSG_ERROR, "Unable to load Font File" << m_Face);
            return;
        }    
	
	
        // Feed Freetype lib with loaded Memory
        FT_Open_Args  open_args;                                                                                                              
    
        open_args.flags      = FT_OPEN_MEMORY;
        open_args.num_params = 0;                                   	
        open_args.memory_base = m_FontData.DataArray;
        open_args.memory_size  = m_FontData.Size;
    
        //Load the FONT Face
        if ( FT_Open_Face( library, &open_args , 0, (FT_Face*)(&m_FreeTypeFace)) == 0 )
        {}
        else
        { 	_LOG(MSG_ERROR, "FT_New_Face failed.There is probably a problem with Loaded Memory.");
            return;
        }
    
        FT_Set_Char_Size( (FT_Face)m_FreeTypeFace, 0  , 0  , 0, 0);
    
        FT_Set_Pixel_Sizes( (FT_Face)m_FreeTypeFace,				 /* handle to face object */ 
                       m_Width,						 /* pixel_width */ 
                       m_Height);
		
		if( m_Face.compare("DroidSansFallback") == 0)
		{

			if(FileSys.GetFile("fonts/DroidSans.ttf", m_AltFontData))
			{
				open_args.flags      = FT_OPEN_MEMORY;
				open_args.num_params = 0;                                   	
				open_args.memory_base = m_AltFontData.DataArray;
				open_args.memory_size  = m_AltFontData.Size;
	        
				if (FT_Open_Face( library, &open_args , 0, (FT_Face*)(&m_AltFreeTypeFace)) == 0)
				{
					FT_Set_Char_Size( (FT_Face)m_AltFreeTypeFace, 0  , 0  , 0, 0);
	            
					FT_Set_Pixel_Sizes( (FT_Face)m_AltFreeTypeFace,				 /* handle to face object */ 
								m_Width,											 /* pixel_width */ 
								m_Height);
				}
			}
		}
    }
    
    
	// allocate a texture name 
	glGenTextures( 1, &m_Texture );
	glBindTexture( GL_TEXTURE_2D, m_Texture );	
    
	// allocate blank pixels for texture 
	GLubyte* TextureData = new GLubyte[ 4 * m_TexWidth * m_TexHeight];
	
#ifdef PLATFORM_WIN32
	ZeroMemory(TextureData,((m_TexWidth * m_TexHeight * 4) *  sizeof(unsigned char)));	// Clear Storage Memory
#else
	for(int i =0; i < m_TexWidth * m_TexHeight * 4 ; i++)
		TextureData[i] = 0;
#endif	
    
#ifdef WIN32
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
#endif
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
	glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );	
	
	while(glGetError() != GL_NO_ERROR);		// clear previous errors
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_TexWidth , m_TexHeight, 0, 
                 GL_RGBA , GL_UNSIGNED_BYTE, TextureData);
	GLenum Errorcode = glGetError();
	if (Errorcode != GL_NO_ERROR)    
		int testbreak = 0;	
    
    delete [] TextureData; 
	TextureData = NULL;				
	glBindTexture( GL_TEXTURE_2D, 0 );
	_LOG(MSG_LOAD, "TTF FONT LOADED...");
    
    
	return;
}




void	oglFont::Render(	const std::wstring& Text, const math::Vec2& Pos, const math::Vec2& Size, unsigned int Flags, COLOR		Color, bool		Clip)
{
    
    if (Size.x <= 0.00001f) // gonna cause a crash!
		return ;	
    
	static	GlyphData*			GlyphA[MAX_CHAR];
	static	LineData			LineA[MAX_LINES];
	int							LineC=0, GlyphC=0;
	if (Text.empty()) return;
    
	OglGraphics*		pGraph = GetOGLGraphics();
	int					ScreenW = pGraph->GetScreenWidth();
	int					ScreenH = pGraph->GetScreenHeight();
    
	//pGraph->GetDevice()->SetTextureStageState( 0, D3DTSS_MAGFILTER, D3DTEXF_LINEAR);
	//pGraph->GetDevice()->SetTextureStageState( 0, D3DTSS_MINFILTER, D3DTEXF_LINEAR);
	//pGraph->GetDevice()->SetTextureStageState( 0, D3DTSS_MIPFILTER, D3DTEXF_LINEAR);
    
    
	BuildGlyphData(Text, int(Size.x * ScreenW), int(Size.y* ScreenH), LineA, GlyphA, LineC, GlyphC, true);
	RenderGlyphData(Pos - math::Vec2(0, GraphicsUtil::InvH * 4.0f  )  , Size, Color, LineA, GlyphA, LineC, GlyphC, Flags, true);
}

void		oglFont::SetTexture()
{
	
    if(m_Texture)
	{
#ifdef _OPENGLES2
        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, m_Texture);
        const GLuint TEXTURE_LOC = 0;
        glUniform1i(TEXTURE_LOC, 0);
        
#else
        glBindTexture( GL_TEXTURE_2D, m_Texture );
		glEnable( GL_TEXTURE_2D );
#endif
	}
	
	/*	
     // enable this to see the texture for debugging purpose.
     Graphics::Vertex2D		VertA[6];
     GraphicsUtil::FillQuad(math::Vec2(0,0), math::Vec2(1,1), math::Vec2(0,0), math::Vec2(1,1), 0xFFFFFFFF, VertA);
     
     GetGraphics()->SetRenderState( Graphics::RS_SRC_BLEND,  Graphics::RV_BLEND_SRC_ALPHA );
     GetGraphics()->SetRenderState( Graphics::RS_DST_BLEND,  Graphics::RV_BLEND_INVSRC_ALPHA );
     GetGraphics()->DrawVertex2D(VertA, 6);
     */
	
}

inline int	oglFont::NextPowerof2 (int Num )
{
	int rval=1;
	// rval<<=1 Is A Prettier Way Of Writing rval*=2; 
	while(rval<Num) rval<<=1;

	if(rval > 1024)
		rval = 1024; // dun comsume too much memory

	return rval;
}

void	oglFont::CacheGlyphs(bool	CopyRect)
{
	FT_Face FaceRef;
	if (m_CacheGlyphC <= 0) return;
    
	//error = FT_Load_Char( face, text[n], FT_LOAD_RENDER ); 
	unsigned int C = m_GlyphMap.GetElementC();
	for (unsigned int z = 0; z <  C; z++)
	{
		GlyphData*	pGlyph = m_GlyphMap.GetElementByIndex(z);
		
        
		/* retrieve glyph index from character code */	 
		FT_UInt Glyph_index; 
		Glyph_index = FT_Get_Char_Index( (FT_Face)m_FreeTypeFace, pGlyph->Glyph  ); 
		if(Glyph_index == 0)
		{
			// can't find that Glyph in the font file?
			// try Altanative font
			Glyph_index = FT_Get_Char_Index( (FT_Face)m_AltFreeTypeFace, pGlyph->Glyph); 
            
            if(Glyph_index == 0)                                    
                FaceRef = (FT_Face)m_FreeTypeFace; // we can't find on both , just show dmann square.
            else                
                FaceRef = (FT_Face)m_AltFreeTypeFace;  // found on altanative font file
		} 
		else
		{
			FaceRef = (FT_Face)m_FreeTypeFace;
		}
        
		/* load glyph image into the slot (erase previous one) */ 
		if( FT_Load_Glyph( FaceRef, Glyph_index, FT_LOAD_DEFAULT ) == 0)
        {}
        else
        {
            m_CacheGlyphC = 0;
			return;
        }
        
		/* convert to an anti-aliased bitmap */  
		if(FT_Render_Glyph( FaceRef->glyph, FT_RENDER_MODE_NORMAL )== 0)
        {}
        else
        {
            m_CacheGlyphC = 0;
			return;
        }
        
		
		//GLint CurrentTexture = 0;
		//glGetIntegerv(GL_TEXTURE_2D_BINDING, &CurrentTexture);
		glBindTexture( GL_TEXTURE_2D, m_Texture );
		
		
		FT_GlyphSlot	FreeTypeGlyhSlot = FaceRef->glyph;
		FT_Bitmap       &Bitmap          =  FreeTypeGlyhSlot->bitmap;
        
		int Width  = Bitmap.width + 2;
		int Height = Bitmap.rows + 2;
        
		Width  =  (int)UTIL_HIGH((int)m_Height+2,(int)Bitmap.width);
		Height =  (int)UTIL_HIGH ((int)m_Height+2, (int)Bitmap.rows);
        
		unsigned char* Expanded_data = new unsigned char[ 4 * Width * Height];
        
		// make space first with alpha 0
		for(int j=0; j < 4 * Width * Height ;j++) 
			Expanded_data[j] = 0;
		
		int DataJ = 0;
		int DataI = 0;
		int DataIndex = 0;
		int ImageIndex  = 0;     
		
		for(int j=0; j <Bitmap.rows;j++) 
		{
			for(int ii=0; ii < Bitmap.width; ii++)
			{
				ImageIndex = (ii+ (j * Bitmap.width) );
				DataIndex = (DataI + (DataJ * Width * 4) );
                
				float Temp =Bitmap.buffer[ImageIndex];
             	Expanded_data[DataIndex+3] = (unsigned char)Temp;	  // Alpha
                
				/*
                Expanded_data[DataIndex+4] = 0;
                Expanded_data[DataIndex+4+1] = 0;
                Expanded_data[DataIndex+4+2] = 0;
                Expanded_data[DataIndex+4+3] = 32; //UTIL_RANGE(Temp,0,255.0f*0.5f);
                
                Expanded_data[DataIndex+8] = 0;
                Expanded_data[DataIndex+8+1] = 0;
                Expanded_data[DataIndex+8+2] = 0;
                Expanded_data[DataIndex+8+3] = 32; //UTIL_RANGE(Temp,0,255.0f*0.5f);
                
                Expanded_data[DataIndex+ (Width * 4)] = 0;
                Expanded_data[DataIndex+(Width * 4)+1] = 0;
                Expanded_data[DataIndex+(Width * 4)+2] = 0;
                Expanded_data[DataIndex+(Width * 4)+3] = 32; //UTIL_RANGE(Temp,64,125.0f);
                
                Expanded_data[DataIndex+ (Width * 8)] = 0;
                Expanded_data[DataIndex+(Width * 8)+1] = 0;
                Expanded_data[DataIndex+(Width * 8)+2] = 0;
                Expanded_data[DataIndex+(Width * 8)+3] = 32; //UTIL_RANGE(Temp,64,125.0f);

				// CHIT: I copy this code from dxfont .. when i want to add border to oglfont
				// I need to do something like this.
				if (m_AddBorder)
				{
					Edge |= ((GetFontPixel(pGlyph->xp + x - 1, pGlyph->yp+y))) > 0;
					Edge |= ((GetFontPixel(pGlyph->xp + x + 1, pGlyph->yp+y))) > 0;
					Edge |= ((GetFontPixel(pGlyph->xp + x, pGlyph->yp+y-1)) ) > 0;
					Edge |= ((GetFontPixel(pGlyph->xp + x, pGlyph->yp+y+1)) ) > 0;

					Edge |= ((GetFontPixel(pGlyph->xp + x - 1, pGlyph->yp+y-1))) > 0;
					Edge |= ((GetFontPixel(pGlyph->xp + x + 1, pGlyph->yp+y-1))) > 0;
					Edge |= ((GetFontPixel(pGlyph->xp + x - 1, pGlyph->yp+y-1)) ) > 0;
					Edge |= ((GetFontPixel(pGlyph->xp + x + 1, pGlyph->yp+y+1)) ) > 0;
				}

                */
                
				Expanded_data[DataIndex]= 255;			// R				      
				Expanded_data[DataIndex+1]= 255;		// G
				Expanded_data[DataIndex+2]= 255;		// B
				Expanded_data[DataIndex+3]= (unsigned char)Temp;		// A
				               
				DataI += 4;				// Move 4 position ( RGBA = 4 positions )
			}
			DataI = 0;
			DataJ++;
		}
        
		int TextExt = 0;
		//if(Bitmap.rows > FreeTypeGlyhSlot->bitmap_top)
        TextExt = Height - FreeTypeGlyhSlot->bitmap_top;
		
		while(glGetError() != GL_NO_ERROR);		// clear previous errors
		glTexSubImage2D(	GL_TEXTURE_2D, 
                        0, 
                        pGlyph->TexX + FreeTypeGlyhSlot->bitmap_left , 
                        pGlyph->TexY + TextExt - 1,
                        Width ,
                        Height ,
                        GL_RGBA, 
                        GL_UNSIGNED_BYTE,
                        Expanded_data );
		
		GLenum Errorcode = glGetError();		// check error
		if (Errorcode != GL_NO_ERROR)   
		{
			int testbreak = 0;	
            m_CacheGlyphC = 0;
			return;
			
		}
		
		delete [] Expanded_data;
        
		pGlyph->Height	= Bitmap.rows + TextExt + 2;
		pGlyph->Width	= Bitmap.width + FreeTypeGlyhSlot->bitmap_left + 1;	
		
        
		//pGlyph->Height = Width;
		//pGlyph->Width	 = Height;
		
		if(Bitmap.rows == 0 && Bitmap.width ==0) // it's SPACE
		{
			pGlyph->Width = (int)(m_Width * 0.35f);
		}
		
		pGlyph->Size.x = float(pGlyph->Width) / float(m_TexWidth);
		pGlyph->Size.y = float(pGlyph->Height) / float(m_TexHeight);
		
	}
    
   	m_CacheGlyphC = 0;  // all cached
    
}

oglFont::GlyphData*		oglFont::GetGlyphIndex(unsigned short Glyph)
{
 	GlyphData*	pGlyph = m_GlyphMap.GetElement(Glyph);
	if (pGlyph) 
		return pGlyph;	
    
	float	Stamp = TIMER().Time();
	const float		MIN_ELAPSED = 0.01f;
	if (!m_pEmpty.empty())
	{
		GlyphData* pGlyph = m_pEmpty[m_pEmpty.size()-1];
		m_pEmpty.resize(m_pEmpty.size()-1);
		pGlyph->Glyph = Glyph;
		pGlyph->TimeStamp = Stamp;
		pGlyph->OffsetX = 0;
		pGlyph->PreOffset = 0;
		pGlyph->ToCache = true;
		m_GlyphMap.AddElement(Glyph, pGlyph);
		m_CacheGlyphC++;
		return pGlyph;
	}
	else
	{
		// run out of Space , have to delte back some
		float	Lowest = 10e10f;
		GlyphData*	pLowest = NULL;
		unsigned int C = m_GlyphMap.GetElementC();
		for (unsigned int i = 0; i <  C; i++)
		{
			GlyphData*	pGlyph = m_GlyphMap.GetElementByIndex(i);			
			float	Time = Stamp - pGlyph->TimeStamp;
			if (Time < Lowest && Time > MIN_ELAPSED)
			{
				pLowest = pGlyph;
				Lowest  = Time;
			}
		}
		
		if (pLowest)
		{
			m_GlyphMap.RemoveElement(pLowest->Glyph);
			pLowest->Glyph = Glyph;
			pLowest->TimeStamp = Stamp;
			pLowest->ToCache = true;
			m_GlyphMap.AddElement(Glyph, pLowest);
			m_CacheGlyphC++;
			return pLowest;
		}
        
	}
	
	return NULL;
}






